﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Ninject;
using NuGet;
using NuGet.Server.DataServices;
using NuGet.Server.Infrastructure;
using MayhemServer.Models;
using MayhemServer.Extensions;

namespace MayhemServer.Services {
    public class PackageService : IPackageService {
        private readonly IServerPackageRepository packageRepository;
        private readonly IPackageDetailService packageDownloadService;
        
        public PackageService(/*IServerPackageRepository packageRepository*/) {
            // There doesn't seem to be a way to inject this
            this.packageRepository = NinjectBootstrapper.Kernel.Get<IServerPackageRepository>(); //packageRepository;
            this.packageDownloadService = new PackageDetailService(this);
        }

        public PageSet<Package> GetPackages(int? pageIndex, int pageSize) {
            var packageCount = 0;
            var packages = packageRepository.GetPackages().Where(p => !p.IsDeveloperPackage());
            var pagedPackages = new List<Package>(pageSize);
            var startPage = (pageIndex.GetValueOrDefault(0) * pageSize) + 1;
            var endPage = startPage + (pageSize) - 1;

            using (var connection = DatabaseService.GetConnection()) {
                var getPackageDetails = new SqlCommand("mayhem_GetPackages", connection);
                getPackageDetails.CommandType = CommandType.StoredProcedure;
                //getPackageDetails.Parameters.AddWithValue("@startPage", startPage);
                //getPackageDetails.Parameters.AddWithValue("@endPage", endPage);

                connection.Open();

                var reader = getPackageDetails.ExecuteReader();
                if (reader.HasRows) {
                    int packageIdIndex = reader.GetOrdinal("PackageId");
                    int packageVersionIndex = reader.GetOrdinal("PackageVersion");
                    int downloadCountIndex = reader.GetOrdinal("TotalDownloadCount");

                    while (reader.Read()) {
                        var packageId = reader.GetString(packageIdIndex);
                        var packageVersion = new Version(reader.GetString(packageVersionIndex));
                        var downloadCount = reader.GetInt32(downloadCountIndex);
                        var package = packages.Where(p => string.Equals(p.Id, packageId, StringComparison.InvariantCultureIgnoreCase) && p.Version == packageVersion).FirstOrDefault();
                        if (package != null) {
                            var p = packageRepository.GetMetadataPackage(package);
                            p.DownloadCount = downloadCount;
                            pagedPackages.Add(p);
                        }
                    }

                    if (reader.NextResult() && reader.Read()) {
                        packageCount = reader.GetInt32(0);
                    }
                }
            }

            return new PageSet<Package>(pagedPackages.ToArray(), pageIndex.GetValueOrDefault(0), pageSize, packageCount);
        }

        public PackageDetails GetPackageDetails(string id) {
            var packages = packageRepository.FindPackagesById(id);
            if (packages.Count() == 0) {
                return null;
            }

            var packageList = new List<Package>(packages.Count());
            foreach (var package in packages) {
                packageList.Add(packageRepository.GetMetadataPackage(package));
            }
            
            packageDownloadService.FillDetails(packageList);

            var downloadCount = packageList.Sum(p => p.VersionDownloadCount);
            foreach (var package in packageList) {
                package.DownloadCount = downloadCount;
            }

            var orderedPackages = packageList.OrderBy(p => p.IsLatestVersion).OrderByDescending(p => p.LastUpdated);
            var firstPackage = orderedPackages.First();
            List<string> events = new List<string>();
            List<string> reactions = new List<string>();

            using (var connection = DatabaseService.GetConnection()) {
                var getPackageDetails = new SqlCommand("SELECT Events, Reactions FROM mayhem_PackageDetail WHERE PackageId = @PackageId AND PackageVersion = @PackageVersion", connection);
                getPackageDetails.Parameters.AddWithValue("@PackageId", firstPackage.Id);
                getPackageDetails.Parameters.AddWithValue("@PackageVersion", firstPackage.Version);

                connection.Open();

                var reader = getPackageDetails.ExecuteReader();
                if (reader.Read()) {
                    var eventsList = reader.GetString(reader.GetOrdinal("Events")).Split(new [] {','}, StringSplitOptions.RemoveEmptyEntries);
                    var reactionsList = reader.GetString(reader.GetOrdinal("Reactions")).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    eventsList.ToList().ForEach(e => events.Add(e.Trim()));
                    reactionsList.ToList().ForEach(r => reactions.Add(r.Trim()));

                    events.Sort();
                    reactions.Sort();
                }
            }

            return new PackageDetails(events, reactions, orderedPackages);
        }

        public Package GetPackage(string id) {
            var package = packageRepository.FindPackage(id);
            if (package == null) {
                return null;
            }

            var p = packageRepository.GetMetadataPackage(package);

            packageDownloadService.FillDetails(p);

            return p;
        }

        public Package GetPackage(string id, string version) {
            var package = packageRepository.FindPackage(id, version);
            if (package == null) {
                return null;
            }

            var p = packageRepository.GetMetadataPackage(package);

            packageDownloadService.FillDetails(p);

            return p;
        }
    }
}